
package com.hengyue.system.service.impl;

import com.hengyue.common.util.DictUtils;
import com.hengyue.common.utils.StringUtils;
import com.hengyue.system.domain.entity.SysDictData;
import com.hengyue.system.domain.entity.SysDictType;
import com.hengyue.system.repository.SysDictDataRepository;
import com.hengyue.system.repository.SysDictTypeRepository;
import com.hengyue.system.service.ISysDictTypeService;
import jakarta.annotation.PostConstruct;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SysDictTypeServiceImpl implements ISysDictTypeService {

    private final SysDictTypeRepository dictTypeRepository;

    private final SysDictDataRepository dictDataRepository;


    /**
     * 项目启动时，初始化字典到缓存
     */
    @PostConstruct
    public void init() {
        loadingDictCache();
    }


    @Override
    public Page<SysDictType> selectDictTypeList(SysDictType dictType, Pageable pageable) {
        return dictTypeRepository.findAll((root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (StringUtils.isNotEmpty(dictType.getDictName())) {
                predicates.add(cb.like(root.get("dictName"), "%" + dictType.getDictName() + "%"));
            }
            if (StringUtils.isNotEmpty(dictType.getDictType())) {
                predicates.add(cb.equal(root.get("dictType"), dictType.getDictType()));
            }
            if (StringUtils.isNotEmpty(dictType.getStatus())) {
                predicates.add(cb.equal(root.get("status"), dictType.getStatus()));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        }, pageable);
    }

    @Override
    public List<SysDictType> selectDictTypeAll() {
        return dictTypeRepository.findAll();
    }

    /**
     * 根据字典类型查询字典数据
     *
     * @param dictType 字典类型
     * @return 字典数据集合信息
     */
    @Override
    @Transactional
    public List<SysDictData> selectDictDataByType(String dictType) {

        // 1. 先尝试从缓存获取
        List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
        if (dictDatas != null) {
            return dictDatas;
        }

        // 2. 缓存没有则从数据库查询
        dictDatas = dictDataRepository.findByDictType(dictType);

        // 3. 存入缓存并返回结果
        if (dictDatas != null && !dictDatas.isEmpty()) {
            DictUtils.setDictCache(dictType, dictDatas);
        }

        return dictDatas != null ? dictDatas : Collections.emptyList();

    }

    @Override
    public SysDictType selectDictTypeById(Long dictId) {
        return dictTypeRepository.findById(dictId).orElse(null);
    }

    @Override
    public SysDictType selectDictTypeByType(String dictType) {
        return dictTypeRepository.findByDictType(dictType);
    }

    @Override
    @Transactional
    public List<String> deleteDictTypeByIds(Long[] dictIds) {
        // 1. 批量获取所有要删除的字典类型
        List<SysDictType> dictTypesToDelete = dictTypeRepository.findAllById(Arrays.asList(dictIds));

        // 2. 收集所有dictType并查询哪些有数据引用
        List<String> dictTypeCodes = dictTypesToDelete.stream()
                .map(SysDictType::getDictType)
                .toList();
        Set<String> referencedTypes = dictDataRepository.findDictTypesWithData(dictTypeCodes);

        // 3. 分类处理可删除和不可删除的记录
        Map<Boolean, List<SysDictType>> partitionedDictTypes = dictTypesToDelete.stream()
                .collect(Collectors.partitioningBy(dictType -> !referencedTypes.contains(dictType.getDictType())));

        // 4. 处理可删除的记录
        List<SysDictType> deletableDictTypes = partitionedDictTypes.get(true);
        if (!deletableDictTypes.isEmpty()) {
            deletableDictTypes.forEach(dictType -> DictUtils.removeDictCache(dictType.getDictType()));
            dictTypeRepository.deleteAllByIdInBatch(
                    deletableDictTypes.stream()
                            .map(SysDictType::getId)
                            .toList()
            );
        }
        // 5. 返回不可删除的字典类型code
        return partitionedDictTypes.get(false).stream()
                .map(SysDictType::getCode)
                .toList();
    }

    @Override
    public void loadingDictCache() {

        Map<String, List<SysDictData>> dictDataMap = dictDataRepository.findByStatus("0")
                .stream().collect(Collectors.groupingBy(SysDictData::getDictType));
        // 实现缓存加载逻辑
        for (Map.Entry<String, List<SysDictData>> entry : dictDataMap.entrySet()) {
            DictUtils.setDictCache(entry.getKey(), entry.getValue()
                    .stream().sorted(Comparator.comparing(SysDictData::getDictSort))
                    .collect(Collectors.toList()));
        }
    }

    @Override
    public void clearDictCache() {
        // 实现缓存清除逻辑
        DictUtils.clearDictCache();
    }

    @Override
    public void resetDictCache() {
        clearDictCache();
        loadingDictCache();
    }

    @Override
    @Transactional
    public void insertDictType(SysDictType dictType) {
        dictTypeRepository.save(dictType);
        DictUtils.setDictCache(dictType.getDictType(), null);
    }

    @Override
    @Transactional
    public void updateDictType(SysDictType dictType) {
        dictTypeRepository.save(dictType);
        List<SysDictData> dictDataList = dictDataRepository.findByDictType(dictType.getDictType());
        DictUtils.setDictCache(dictType.getDictType(), dictDataList);
    }

    @Override
    public boolean checkDictTypeUnique(SysDictType dictType) {

        Long dictId = dictType.getId() == null ? -1L : dictType.getId();
        // 查询是否存在相同dictType或code的记录（排除当前记录）
        boolean exists = dictTypeRepository.exists((root, query, cb) ->
                cb.and(
                        cb.or(
                                cb.equal(root.get("dictType"), dictType.getDictType()),
                                cb.equal(root.get("code"), dictType.getCode())
                        ),
                        cb.notEqual(root.get("id"), dictId)
                )
        );

        return !exists;
    }
}
